/* Global declarations for the khipu API.

   This file is part of khipu.

   khipu is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#ifndef __KHIPU__
#define __KHIPU__   1

#include "integer.hpp"
#include "floatp.hpp"
#include "memory.hpp"
#include "cons.hpp"
#include "symbol.hpp"
#include "stream.hpp"
#include "array.hpp"
#include "str.hpp"
#include "table.hpp"
#include "thread.hpp"
#include "tuple.hpp"
#include "function.hpp"
#include "io.hpp"
#include "xtime.hpp"
#include "coro.hpp"
#include "bytecode.hpp"
#include "builtins.hpp"
#include "event.hpp"
#include "types.hpp"
#include "custom.hpp"

KP_DECLS_BEGIN

KP_EXPORT bool khipu_init (void);

template <class T, bool B>
struct objtype_helper
{
  static void* unmask_ptr (object obj)
    {
      return (unmask (obj));
    }

  static bool check_type (object)
    {
      return (true);
    }
};

template <class T>
struct objtype_helper<T, true>
{
  static custom_base* unmask_ptr (object obj)
    {
      return (as_custom (obj));
    }

  static bool check_type (object obj)
    {
      return (T::type_p (obj));
    }
};

template <class T>
struct objtype
{
  typedef T* type;
  static const int code = T::code;

  static inline type get (object obj)
    {
      return ((type)objtype_helper<T,
        code == typecode::CUSTOM>::unmask_ptr (obj));
    }
};

template <>
struct objtype<int>
{
  typedef int type;
  static const int code = typecode::INT;

  static inline type get (object obj)
    {
      return (as_int (obj));
    }
};

template <>
struct objtype<char>
{
  typedef uint32_t type;
  static const int code = typecode::CHAR;

  static inline type get (object obj)
    {
      return (as_char (obj));
    }
};

template <>
struct objtype<float>
{
  typedef double type;
  static const int code = typecode::FLOAT;

  static inline type get (object obj)
    {
      return (as_float (obj));
    }
};

template <class T>
bool as (object obj, typename objtype<T>::type& out)
{
  int tx = objtype<T>::code, itp = itype (obj);

  if ((tx == itp || (tx == typecode::BVECTOR && itp == typecode::STR)) &&
      objtype_helper<T, objtype<T>::code == typecode::CUSTOM>::check_type (obj))
    {
      out = objtype<T>::get (obj);
      return (true);
    }
  else if (itp == typecode::INSTANCE)
    {
      object tmp = builtin_member (obj);
      itp = itype (tmp);

      if (tx == itp ||
          (tx == typecode::BVECTOR && itp == typecode::STR))
        {
          out = objtype<T>::get (tmp);
          return (true);
        }
    }

  return (false);
}

template <class T>
typename objtype<T>::type as (object obj)
{
  typename objtype<T>::type ret = 0;
  (void)as<T> (obj, ret);
  return (ret);
}

KP_DECLS_END

#endif
